package com.secsbrain.deploy.utils.command;

import org.apache.commons.exec.*;

import java.io.InputStream;
import java.util.Map;

/**
 * 简单的命令执行工具
 * Created by yangyang.zhang on 2017/8/22.
 */
public class CommandExecutor {

    private static final long TIMEOUT = 1000 * 60 * 15;

    private long timeout;

    public CommandExecutor() {

    }

    public CommandExecutor(long timeout) {
        this.timeout = timeout;
    }

    /**
     * 同步执行命令 忽略结果
     *
     * @param command   命令
     * @param arguments 参数
     */
    public void execSync(String command, String arguments) {
        execSync(command, arguments, null, null, null);
    }

    /**
     * 同步执行
     *
     * @param command   命令
     * @param arguments 参数
     * @param callback  处理数据的回调
     */
    public void execSync(String command, String arguments, ExecuteStrategy callback) {
        execSync(command, arguments, null, null, callback);
    }

    /**
     * 同步执行
     *
     * @param command   命令
     * @param arguments 参数
     * @param env       环境变量
     * @param callback  处理数据的回调
     */
    public void execSync(String command, String arguments, Map<String, String> env, ExecuteStrategy callback) {
        execSync(command, arguments, null, env, callback);
    }

    /**
     * 同步执行
     *
     * @param command     命令
     * @param arguments   参数
     * @param inputStream 输入流
     * @param callback    处理数据的回调
     */
    public void execSync(String command, String arguments, InputStream inputStream, Map<String, String> env, ExecuteStrategy callback) {
        exec(false, command, arguments, inputStream, env, callback);
    }

    /**
     * 异步执行执行命令
     *
     * @param command   命令
     * @param arguments 参数
     */
    public ExecuteResultHandler execAsync(String command, String arguments) {
        return execAsync(command, arguments, null, null);
    }

    /**
     * 异步执行执行命令
     *
     * @param command   命令
     * @param arguments 参数
     * @param callback  处理数据的回调
     */
    public ExecuteResultHandler execAsync(String command, String arguments, ExecuteStrategy callback) {
        return execAsync(command, arguments, null, callback);
    }

    /**
     * 异步执行命令
     *
     * @param command     命令
     * @param arguments   参数
     * @param inputStream 输入流
     * @param callback    处理数据的回调
     * @return
     */
    public ExecuteResultHandler execAsync(String command, String arguments, InputStream inputStream, ExecuteStrategy callback) {
        return exec(true, command, arguments, inputStream, null, callback);
    }

    /**
     * 执行命令，持续交互，不处理命令中的 引号(" ')和空格
     *
     * @param async       是否异步
     * @param command     命令
     * @param arguments   参数
     * @param inputStream 输入流
     * @param env         环境变量
     * @param callback    处理数据的回调
     * @see ExecuteStrategy
     * @see ExecuteStrategyAdapter
     */
    public ExecuteResultHandler exec(boolean async, String command, String arguments, InputStream inputStream, Map<String, String> env, ExecuteStrategy callback) {
        CommandLine cmdLine = CommandLine.parse(command);
        cmdLine.addArguments(arguments, false);
        DefaultExecutor executor = new DefaultExecutor();
        CustomStreamHandler pumpStreamHandler = new CustomStreamHandler(callback, inputStream);
        executor.setStreamHandler(pumpStreamHandler);
        ExecuteWatchdog executeWatchdog = new ExecuteWatchdog(getTimeout());
        executor.setWatchdog(executeWatchdog);
        try {
            if (async) {
                if (callback == null) {
                    DefaultExecuteResultHandler defaultExecuteResultHandler = new DefaultExecuteResultHandler();
                    executor.execute(cmdLine, env, defaultExecuteResultHandler);
                    return defaultExecuteResultHandler;
                }
                executor.execute(cmdLine, env, callback);
            } else {
                int exitValue = executor.execute(cmdLine, env);
                callback.handleResult(exitValue);
            }
        } catch (Exception e) {
            if (callback == null) {
                throw new RuntimeException("执行命令遇到异常！", e);
            }
            callback.handleException(e);
        }
        return callback;
    }

    public long getTimeout() {
        return this.timeout > 0 ? this.timeout : TIMEOUT;
    }

    public long setTimeout(long timeout) {
        return this.timeout = timeout;
    }

}
